// generator生成器函数
function* foo() {
  try {
    let res = yield 'hello';
    console.log(res);
  } catch (e) {
    console.log(e);
  }
}
// 生成 generator 对象
const generator = foo();
console.log(generator.next('f'));
console.log(generator.throw('err'));

/* 
特性
1 调用foo()方法不会直接执行foo函数
2 调用generator.next()方法,返回yield后面的值
3 yield只是暂停生成器函数的执行，直到下次调用generator.next()
4 下次执行next会在生成器函数中上次的yield接收到这个参数
5 第一次调用next('XXX')传参不会在生成器函数被yield返回
*/

//

// 利用生成器完成更优的异步编程体验

const ajax = value => {
  return new Promise((resovle, rej) => {
    setTimeout(() => {
      resovle(value);
    }, 1000);
  });
};

function* mian() {
  const name = yield ajax('guolihang');
  console.log(name); // 彻底消灭Promise的回调 近乎于同步的编程
  const age = yield ajax('18');
  console.log(age);
  console.log(2334);
}

// const result = g.next();
// result.value.then(name => {
//   if (result.done) return;
//   const result2 = g.next(name);
//   result2.value.then(age => {
//     console.log(result2.done);
//     if (result2.done) return;
//     g.next(age);
//     // .... 采用递归
//   });
// });

// 采用递归的方式实现生成器函数执行器
// co库在15年之前就是这样分装 async/await 的实现机制也是类似
// function co(generator) {
//   const g = generator();
//   function handleResult(result) {
//     if (result.done) return;
//     result.value.then(
//       value => {
//         handleResult(g.next(value));
//       },
//       err => {
//         g.throw(err);
//       },
//     );
//   }
//   handleResult(g.next());
// }
// co(mian);

// async await 的内部实现方式
async function fn(args) {
  // ...
}

function fn(args) {
  return spawn(function* () {
    // ...
  });
}

function spawn(genF) {
  return new Promise(function (resolve, reject) {
    const gen = genF();
    function step(nextF) {
      let next;
      try {
        next = nextF();
      } catch (e) {
        return reject(e);
      }
      if (next.done) {
        return resolve(next.value);
      }
      Promise.resolve(next.value).then(
        function (v) {
          step(function () {
            return gen.next(v);
          });
        },
        function (e) {
          step(function () {
            return gen.throw(e);
          });
        }
      );
    }
    step(function () {
      return gen.next(undefined);
    });
  });
}
